package com.iteaj.iot;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ServiceLoaderUtil;
import com.iteaj.iot.business.ProtocolHandleFactory;
import com.iteaj.iot.client.ClientComponent;
import com.iteaj.iot.client.ClientConnectProperties;
import com.iteaj.iot.client.ClientMessage;
import com.iteaj.iot.client.IotClient;
import com.iteaj.iot.codec.filter.ClientRegister;
import com.iteaj.iot.codec.filter.RegisterParams;
import com.iteaj.iot.event.AsyncStatusEventListener;
import com.iteaj.iot.event.FrameworkEventListener;
import com.iteaj.iot.event.IotEvent;
import com.iteaj.iot.server.ServerComponent;
import com.iteaj.iot.server.ServerMessage;
import io.netty.channel.Channel;

import java.util.*;

public class FrameworkManager {

    /**
     * 线程管理器
     */
    private IotThreadManager threadManager;

    /**
     * 组件工厂
     */
    private static ComponentFactory componentFactory;


    /**
     * 客户端处理工厂
     */
    private static ProtocolHandleFactory handleFactory;

    /**
     * 协议超时管理器
     */
    private ProtocolTimeoutManager timeoutManager;

    /**
     * 设备管理工厂
     */
    private DeviceManagerFactory deviceManagerFactory;

    /**
     * 监听列表
     */
    private Set<FrameworkEventListener> listeners;

    private static FrameworkManager instance = new FrameworkManager();

    protected static FrameworkManager getFrameworkManager() {
        return instance;
    }

    protected FrameworkManager() {
        this.listeners = new HashSet<>(8);
    }

    public static FrameworkManager getInstance() {
        return instance;
    }

    public void build(List<FrameworkEventListener> listeners, IotThreadManager threadManager
            , ComponentFactory componentFactory, ProtocolHandleFactory handleFactory
            , ProtocolTimeoutManager timeoutManager, DeviceManagerFactory deviceManagerFactory) {
        this.threadManager = threadManager;
        this.timeoutManager = timeoutManager;
        this.deviceManagerFactory = deviceManagerFactory;
        FrameworkManager.handleFactory = handleFactory;
        getFrameworkManager().listeners.addAll(listeners);
        FrameworkManager.componentFactory = componentFactory;
    }

    /**
     * 关闭指定组件
     * @see ProtocolHandleFactory#removes(Class)
     * @see ProtocolTimeoutManager#remove(ProtocolTimeoutStorage)
     * @param messageClazz
     * @return
     */
    public static Optional<Close> close(Class<? extends Message> messageClazz) {
        FrameworkComponent component = getComponentFactory().get(messageClazz);
        boolean close = getComponentFactory().close(messageClazz);
        if(close) {
            Set removes = getHandleFactory().removes(messageClazz);
            return Optional.of(new Close(component, removes));
        }

        return Optional.empty();
    }

    /**
     * 启用指定组件
     * @param messageClazz
     */
    public static void start(Class<?> messageClazz) {
        getComponentFactory().start(messageClazz);
    }

    /**
     * 启用指定组件
     * @param component
     */
    public static void start(FrameworkComponent component) {
        start(component.getMessageClass());
    }

    /**
     * 停止指定组件
     * @param messageClazz
     * @return
     */
    public static boolean stop(Class<? extends Message> messageClazz) {
        return getComponentFactory().stop(messageClazz);
    }

    /**
     * 停止指定组件
     * @param component
     * @return
     */
    public static boolean stop(FrameworkComponent component) {
        return stop(component.getMessageClass());
    }

    /**
     * 注册组件
     * @param component
     */
    public static Register register(FrameworkComponent component) {
        getComponentFactory().register(component);
        return new Register(component);
    }

    /**
     * 注册handle组件
     * @param handle
     */
    public static FrameworkManager register(ProtocolHandle handle) {
        getHandleFactory().register(handle); return instance;
    }

    /**
     * 发布事件
     * @param event
     */
    public static void publishEvent(IotEvent event) {
        FrameworkManager instance = getFrameworkManager();
        instance.listeners.forEach(listener -> {
            if(listener.isMatcher(event)) {
                if(listener instanceof AsyncStatusEventListener) {
                    getInstance().threadManager.getExecutorService()
                            .execute(() -> listener.onEvent(event));
                } else {
                    listener.onEvent(event);
                }
            }
        });
    }

    /**
     * 通过端口号获取Tcp组件
     * @param port
     * @param <T>
     * @return
     */
    public static <T extends ServerComponent> T getTcpComponent(Integer port) {
        return (T) getComponentFactory().get(PortType.Tcp, port);
    }

    /**
     * 通过端口号获取Udp组件
     * @param port
     * @param <T>
     * @return
     */
    public static <T extends ServerComponent> T getUdpComponent(Integer port) {
        return (T) getComponentFactory().get(PortType.Udp, port);
    }

    /**
     * 获取组件
     * @param messageClass {@link Message}
     * @param <T>
     * @return
     */
    public static <T extends FrameworkComponent> T getComponent(Class<? extends Message> messageClass) {
        return (T) getComponentFactory().get(messageClass);
    }

    /**
     * 获取服务端组件
     * @param messageClass {@link ServerMessage}
     * @param <T>
     * @return
     */
    public static <T extends ServerComponent> T getServerComponent(Class<? extends ServerMessage> messageClass) {
        return (T) getComponentFactory().get(messageClass);
    }

    /**
     * 获取客户端组件
     * @param messageClass {@link ClientMessage}
     * @param <T>
     * @return
     */
    public static <T extends ClientComponent> T getClientComponent(Class<? extends ClientMessage> messageClass) {
        return (T) getComponentFactory().get(messageClass);
    }


    /**
     * 获取一个设备管理实例
     * @param messageClass {@link ServerMessage}
     * @return
     */
    public static DeviceManager getDeviceManager(Class<? extends Message> messageClass){
        FrameworkComponent component = FrameworkManager.getComponent(messageClass);
        return  component != null ? ((ServerComponent) component).getDeviceManager() : null;
    }

    /**
     * 获取指定客户端的Tcp连接
     * @param equipCode 客户端编号
     * @param clazz 在哪个服务端组件下查找
     * @return Tcp连接
     */
    public static Channel getChannel(String equipCode, Class<? extends ServerMessage> clazz) {
        DeviceManager deviceManager = getDeviceManager(clazz);
        return deviceManager != null ? (Channel) deviceManager.find(equipCode) : null;
    }

    /**
     * 获取指定组件默认客户端
     * @param messageClazz {@link ClientMessage}
     * @return
     */
    public static IotClient getClient(Class<? extends ClientMessage> messageClazz) {
        ClientComponent clientComponent = getClientComponent(messageClazz);
        return clientComponent != null ? clientComponent.getClient() : null;
    }

    /**
     * 获取客户端
     * @param config {@link ClientConnectProperties} or {@link ClientConnectProperties#connectKey()}
     * @param messageClazz
     * @return
     */
    public static IotClient getClient(Object config, Class<? extends ClientMessage> messageClazz) {
        ClientComponent clientComponent = getClientComponent(messageClazz);
        return clientComponent != null ? clientComponent.getClient(config) : null;
    }

    /**
     * 获取业务处理器
     * @param clazz
     * @return
     */
    public static ProtocolHandle getProtocolHandle(Class<? extends Protocol> clazz) {
        return getHandleFactory().getHandle(clazz);
    }

    /**
     * 创建设备管理器
     * @param component
     * @return
     */
    public static DeviceManager createDeviceManager(ServerComponent component) {
        if(getInstance().deviceManagerFactory == null) {
            synchronized (getInstance()) {
                if(getInstance().deviceManagerFactory == null) {
                    List<DeviceManagerFactory> factories = ServiceLoaderUtil
                            .loadList(DeviceManagerFactory.class, FrameworkManager.class.getClassLoader());
                    if(CollectionUtil.isNotEmpty(factories)) {
                        getInstance().deviceManagerFactory = factories.get(factories.size() - 1);
                    } else {
                        throw new FrameworkException("未指定设备管理器[DeviceManagerFactory]");
                    }
                }
            }
        }

        return getInstance().deviceManagerFactory.createDeviceManager(component);
    }

    /**
     * 返回所有Tcp连接数
     * @return
     */
    public static int getTcp() {
        return getComponentFactory().servers()
                .stream()
                .filter(item -> item.getDeviceManager() instanceof ChannelManager)
                .map(item -> ((ChannelManager) item.getDeviceManager()).size())
                .reduce((a, b) -> a + b).get();
    }

    /**
     * @see ClientRegister#register(Message.MessageHead, RegisterParams)
     * 返回已注册过的tcp连接数
     * @return
     */
    public static int getUseTcp() {
        return getComponentFactory().servers()
                .stream()
                .filter(item -> item.getDeviceManager() instanceof ChannelManager)
                .map(item -> item.getDeviceManager().useSize())
                .reduce((a, b) -> a + b).get();
    }

    public void addListener(FrameworkEventListener listener) {
        this.listeners.add(listener);
    }

    public static ComponentFactory getComponentFactory() {
        return componentFactory;
    }

    public static ProtocolHandleFactory getHandleFactory() {
        return handleFactory;
    }

    public ProtocolTimeoutManager getTimeoutManager() {
        return timeoutManager;
    }

    public static class Register {

        private FrameworkComponent component;

        public Register(FrameworkComponent component) {
            this.component = component;
        }

        public void start() {
            getComponentFactory().start(component.getMessageClass());
        }
    }

    public static class Close {

        private FrameworkComponent component;
        private Set<? extends ProtocolHandle> handles;

        public Close(FrameworkComponent component, Set<? extends ProtocolHandle> handles) {
            this.component = component;
            this.handles = handles;
        }

        public Set<? extends ProtocolHandle> getHandles() {
            return handles;
        }

        public FrameworkComponent getComponent() {
            return component;
        }
    }
}
